package client

import (
	"bytes"
	"errors"
	"io"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/PuerkitoBio/goquery"
)

type client struct {
	cookie  []*http.Cookie //cookie
	header  map[string]string
	referer string
}

func New() *client {
	return &client{
		cookie:  nil,
		header:  map[string]string{},
		referer: "",
	}
}

// addheader
func (this *client) AddHeader(key, val string) {
	this.header[key] = val
}

// delheader
func (this *client) DelHeader(key, val string) {
	delete(this.header, key)
}

func (this *client) GetReferfer() string {
	return this.referer
}

func (this *client) SetReferfer(referer string) {
	this.referer = referer
}

// 取出来 cookie
func (this *client) GetCookie() []*http.Cookie {
	return this.cookie
}

// 设置 cookie
// Cookies string or []*http.Cookie
func (this *client) SetCookie(Cookies any) error {
	cookie := make([]*http.Cookie, 0)
	switch Cookies.(type) {
	case string:
		req, _ := http.NewRequest("", "", nil)
		req.Header.Add("cookie", Cookies.(string))
		cookie = req.Cookies()
	case []*http.Cookie:
		cookie = Cookies.([]*http.Cookie)
	case nil:
	default:
		return errors.New("错误的cookie格式")
	}

	this.cookie = cookie

	return nil
}

//通过 GET 方式
//rawurl url
func (this *client) Get(rawurl string) (*http.Response, error) {
	return this.request(http.MethodGet, rawurl, nil)
}

//通过 POST 方式
//rawurl url
func (this *client) Post(rawurl string, input any) (*http.Response, error) {
	querystr := ""
	switch input.(type) {
	case string:
		querystr = input.(string)
	case url.Values:
		_input := input.(url.Values)
		querystr = _input.Encode()
	}

	if "" == this.header["Content-Type"] {
		this.AddHeader("Content-Type", "application/x-www-form-urlencoded")
	}

	return this.request(http.MethodPost, rawurl, strings.NewReader(querystr))
}

//通过GET|POST方式
//rawurl url
func (this *client) request(method string, rawurl string, body io.Reader) (*http.Response, error) {
	URL, e := url.Parse(rawurl)
	if e != nil {
		return nil, e
	}
	host := URL.Host

	if "" == host {
		return nil, errors.New("host 不对")
	}

	// 代理
	proxy := getProxy()
	if "" != proxy {
		URL.Host = proxy
		rawurl = URL.String()
	}

	req, e := http.NewRequest(method, rawurl, body)
	if e != nil {
		return nil, e
	}

	//附加header
	Header := headerplus()
	for k, rows := range Header {
		for _, v := range rows {
			req.Header.Add(k, v)
		}
	}

	req.Host = host
	for k, v := range this.header {
		req.Header.Add(k, v)
	}

	//设置cookie
	cookies := this.GetCookie()
	for _, cookie := range cookies {
		req.AddCookie(cookie)
	}

	//设置referer
	referer := this.GetReferfer()
	req.Header.Add("referer", referer)

	c := &http.Client{
		Timeout: time.Second * 3600,
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return http.ErrUseLastResponse
		},
	}

	resp := &http.Response{}
	//尝试三次
	for retry := 0; retry < 3; retry++ {
		resp, e = c.Do(req)
		if e != nil {
			time.Sleep(time.Second * 1)
		} else {
			break
		}
	}
	if e != nil {
		return nil, e
	}

	// 存在 Location
	if Location, ok := resp.Header["Location"]; ok && len(Location) > 0 {
		return this.request(method, Location[0], body)
	}

	//保存cookie
	e = this.SetCookie(resp.Cookies())
	if e != nil {
		return nil, e
	}
	//保存 http_referer
	this.SetReferfer(rawurl)

	return resp, nil
}

func (this *client) GetDoc(rawurl string) (*goquery.Document, error) {
	res, e := this.Get(rawurl)
	if e != nil {
		return nil, e
	}

	doc, e := goquery.NewDocumentFromResponse(res)
	if e != nil {
		return nil, e
	}

	return doc, nil
}

func (this *client) PostDoc(rawurl string, input any) (*goquery.Document, error) {
	res, e := this.Post(rawurl, input)
	if e != nil {
		return nil, e
	}
	doc, e := goquery.NewDocumentFromResponse(res)
	if e != nil {
		return nil, e
	}

	return doc, nil
}

//<form action="http://dev.api.etchouse.lejuesf.com/index/upload" method="post" enctype="multipart/form-data">
//  <input type="text" name="api_id" value="Appid">
//  <input type="text" name="api_secret" value="Key">
//  <input type="text" name="name" value="r.Name">
//  <input type="text" name="id_number" value="r.IdCode">
//  <input type="file" name="f1" value="上传文件">
//  <input type="submit" value="提交">
//</form>
func (this *client) PostFileDoc(rawurl string, input any, upfiles map[string]string) (*goquery.Document, error) {
	bodyBuf := &bytes.Buffer{}
	bodyWrite := multipart.NewWriter(bodyBuf)

	switch input.(type) {
	case string:
		_input := input.(string)
		if "" != _input {
			values, e := url.ParseQuery(_input)
			if e != nil {
				return nil, e
			}
			for k, v := range values {
				bodyWrite.WriteField(k, strings.Join(v, ";"))
			}
		}
	case url.Values:
		_input := input.(url.Values)
		for k, v := range _input {
			bodyWrite.WriteField(k, strings.Join(v, ";"))
		}
	}

	for key, val := range upfiles {
		file, e := os.Open(val)
		defer file.Close()
		if e != nil {
			return nil, e
		}
		fileWrite, e := bodyWrite.CreateFormFile(key, val)
		if e != nil {
			return nil, e
		}
		_, e = io.Copy(fileWrite, file)
		if e != nil {
			return nil, e
		}
	}

	bodyWrite.Close() //要关闭，会将w.w.boundary刷写到w.writer中

	this.AddHeader("Content-Type", bodyWrite.FormDataContentType())
	res, e := this.request(http.MethodPost, rawurl, bodyBuf)
	if e != nil {
		return nil, e
	}
	doc, e := goquery.NewDocumentFromResponse(res)
	if e != nil {
		return nil, e
	}

	return doc, nil
}
